# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####

# Script copyright (c) Andreas Esau

import bpy
from bpy.props import (StringProperty, BoolProperty, EnumProperty,
                       FloatProperty, IntProperty, CollectionProperty)
import os
from bpy.app.handlers import persistent
from mathutils import Matrix

bl_info = {
    "name": "Godot Export Manager",
    "author": "Andreas Esau",
    "version": (1, 0),
    "blender": (2, 7, 0),
    "location": "Scene Properties > Godot Export Manager",
    "description": "Godot Export Manager uses the Better Collada Exporter"
    "to manage Export Groups and automatically export the objects groups"
    "to Collada Files.",
    "warning": "",
    "wiki_url": ("http://www.godotengine.org"),
    "tracker_url": "",
    "category": "Import-Export"}


class godot_export_manager(bpy.types.Panel):
    bl_label = "Godot Export Manager"
    bl_space_type = "PROPERTIES"
    bl_region_type = "WINDOW"
    bl_context = "scene"

    bpy.types.Scene.godot_export_on_save = BoolProperty(default=False)

    def draw(self, context):
        """ Draw function for all ui elements """
        layout = self.layout
        scene = bpy.data.scenes[0]
        scene = context.scene

        row = layout.row()
        col = row.column()
        col.prop(scene, "godot_export_on_save", text="Export Groups on save")

        row = layout.row()
        col = row.column(align=True)
        col.operator("scene.godot_add_objects_to_group",
                     text="Add selected objects to Group",
                     icon="COPYDOWN")
        col.operator("scene.godot_delete_objects_from_group",
                     text="Delete selected objects from Group",
                     icon="PASTEDOWN")

        row = layout.row()
        col = row.column()
        col.label(text="Export Groups:")

        row = layout.row()
        col = row.column()

        col.template_list("UI_List_Godot", "dummy", scene,
                          "godot_export_groups", scene,
                          "godot_export_groups_index", rows=1, maxrows=10,
                          type="DEFAULT")

        col = row.column(align=True)
        col.operator("scene.godot_add_export_group", text="", icon="ZOOMIN")
        col.operator("scene.godot_delete_export_group", text="",
                     icon="ZOOMOUT")
        col.operator("scene.godot_export_all_groups", text="", icon="EXPORT")

        if len(scene.godot_export_groups) > 0:
            row = layout.row()
            col = row.column()
            group = scene.godot_export_groups[scene.godot_export_groups_index]
            col.prop(group, "name", text="Group Name")
            col.prop(group, "export_name", text="Export Name")
            col.prop(group, "export_path", text="Export Filepath")

            row = layout.row()
            col = row.column()
            row = layout.row()
            col = row.column()
            col.label(text="Export Settings:")

            col = col.row(align=True)
            col.prop(group, "apply_loc", toggle=True, icon="MAN_TRANS")
            col.prop(group, "apply_rot", toggle=True, icon="MAN_ROT")
            col.prop(group, "apply_scale", toggle=True, icon="MAN_SCALE")

            row = layout.row()
            col = row.column()

            col.prop(group, "use_move_to_center")
            col.prop(group, "use_include_particle_duplicates")
            col.prop(group, "use_mesh_modifiers")
            col.prop(group, "use_tangent_arrays")
            col.prop(group, "use_triangles")
            col.prop(group, "use_copy_images")
            col.prop(group, "use_active_layers")
            col.prop(group, "use_anim")
            col.prop(group, "use_anim_action_all")
            col.prop(group, "use_anim_skip_noexp")
            col.prop(group, "use_anim_optimize")
            col.prop(group, "anim_optimize_precision")
            col.prop(group, "use_metadata")


class UI_List_Godot(bpy.types.UIList):
    """ Custom template_list look """
    def draw_item(self, context, layout, data, item, icon, active_data,
                  active_propname, index):
        col = layout.row(align=True)

        col.label(text=item.name, icon="GROUP")
        col.prop(item, "active", text="")

        op = col.operator("scene.godot_select_group_objects",
                          text="", emboss=False, icon="RESTRICT_SELECT_OFF")
        op.idx = index
        op = col.operator("scene.godot_export_group", text="", emboss=False,
                          icon="EXPORT")
        op.idx = index


class add_objects_to_group(bpy.types.Operator):
    bl_idname = "scene.godot_add_objects_to_group"
    bl_label = "Add Objects to Group"
    bl_description = "Adds the selected Objects to the active group below."

    undo = BoolProperty(default=True)

    def execute(self, context):
        scene = context.scene

        objects_str = ""
        if len(scene.godot_export_groups) > 0:
            for i, object in enumerate(context.selected_objects):
                if object.name not in scene.godot_export_groups[
                        scene.godot_export_groups_index].nodes:
                    node = scene.godot_export_groups[
                        scene.godot_export_groups_index].nodes.add()
                    node.name = object.name
                    if i == 0:
                        objects_str += object.name
                    else:
                        objects_str += ", "+object.name

            self.report({"INFO"}, "{} added to group.".format(objects_str))
            if self.undo:
                bpy.ops.ed.undo_push(message="Objects added to group")
        else:
            self.report({"WARNING"}, "Create a group first.")

        return {"FINISHED"}


class del_objects_from_group(bpy.types.Operator):
    bl_idname = "scene.godot_delete_objects_from_group"
    bl_label = "Delete Objects from Group"
    bl_description = "Delets the selected Objects from the active group below."

    def execute(self, context):
        scene = context.scene

        if len(scene.godot_export_groups) > 0:

            selected_objects = []
            for object in context.selected_objects:
                selected_objects.append(object.name)

            objects_str = ""
            j = 0
            for i, node in enumerate(scene.godot_export_groups[
                    scene.godot_export_groups_index].nodes):
                if node.name in selected_objects:
                    scene.godot_export_groups[
                        scene.godot_export_groups_index].nodes.remove(i)

                    if j == 0:
                            objects_str += object.name
                    else:
                        objects_str += ", "+object.name
                    j += 1

            self.report({"INFO"}, "{} deleted from group.".format(objects_str))
            bpy.ops.ed.undo_push(message="Objects deleted from group")
        else:
            self.report({"WARNING"}, "There is no group to delete from.")

        return {"FINISHED"}


class select_group_objects(bpy.types.Operator):
    bl_idname = "scene.godot_select_group_objects"
    bl_label = "Select Group Objects"
    bl_description = "Will select all group Objects in the scene."

    idx = IntProperty()

    def execute(self, context):
        scene = context.scene
        for object in context.scene.objects:
            object.select = False
        for node in scene.godot_export_groups[self.idx].nodes:
            if node.name in bpy.data.objects:
                bpy.data.objects[node.name].select = True
                context.scene.objects.active = bpy.data.objects[node.name]

        return {"FINISHED"}


class export_groups_autosave(bpy.types.Operator):
    bl_idname = "scene.godot_export_groups_autosave"
    bl_label = "Export All Groups"
    bl_description = "Exports all groups to Collada."

    def execute(self, context):
        scene = context.scene
        if scene.godot_export_on_save:
            for i in range(len(scene.godot_export_groups)):
                if scene.godot_export_groups[i].active:
                    bpy.ops.scene.godot_export_group(idx=i)
        self.report({"INFO"}, "All Groups exported.")
        bpy.ops.ed.undo_push(message="Export all Groups")

        return {"FINISHED"}


class export_all_groups(bpy.types.Operator):
    bl_idname = "scene.godot_export_all_groups"
    bl_label = "Export All Groups"
    bl_description = "Exports all groups to Collada."

    def execute(self, context):
        scene = context.scene

        for i in range(0, len(scene.godot_export_groups)):
            bpy.ops.scene.godot_export_group(idx=i, export_all=True)

        self.report({"INFO"}, "All Groups exported.")

        return {"FINISHED"}


class export_group(bpy.types.Operator):
    bl_idname = "scene.godot_export_group"
    bl_label = "Export Group"
    bl_description = "Exports the active group to destination folder"\
                     " as Collada file."

    idx = IntProperty(default=0)
    export_all = BoolProperty(default=False)

    def copy_object_recursive(self, ob, parent, single_user=True):
        new_ob = bpy.data.objects[ob.name].copy()
        if single_user or ob.type == "ARMATURE":
            new_mesh_data = new_ob.data.copy()
            new_ob.data = new_mesh_data
        bpy.context.scene.objects.link(new_ob)

        if ob != parent:
            new_ob.parent = parent
        else:
            new_ob.parent = None

        for child in ob.children:
            self.copy_object_recursive(child, new_ob, single_user)
        new_ob.select = True

        return new_ob

    def delete_object(self, ob):
        if ob is not None:
            for child in ob.children:
                self.delete_object(child)
            bpy.context.scene.objects.unlink(ob)
            bpy.data.objects.remove(ob)

    def convert_group_to_node(self, group):
        if group.dupli_group is not None:
            for object in group.dupli_group.objects:
                if object.parent is None:
                    object = self.copy_object_recursive(object, object, True)
                    matrix = Matrix(object.matrix_local)
                    object.matrix_local = Matrix()
                    object.matrix_local *= group.matrix_local
                    object.matrix_local *= matrix

            self.delete_object(group)

    def execute(self, context):
        group = context.scene.godot_export_groups

        if not group[self.idx].active and self.export_all:
            return{"FINISHED"}

        for i, object in enumerate(group[self.idx].nodes):
            if object.name in bpy.data.objects:
                pass
            else:
                group[self.idx].nodes.remove(i)
        bpy.ops.ed.undo_push(message="Clear not existent Group Nodes.")

        path = group[self.idx].export_path
        if (path.find("//") == 0 or path.find("\\\\") == 0):
            path = bpy.path.abspath(path)
            path = path.replace("\\", "/")

        if os.path.exists(path) and group[self.idx].export_name != "":
            context.scene.layers = [True] * 20

            if group[self.idx].export_name.endswith(".dae"):
                path = os.path.join(path, group[self.idx].export_name)
            else:
                path = os.path.join(
                    path, "{}.dae".format(group[self.idx].export_name))

            hide_select = []
            for object in context.scene.objects:
                hide_select.append(object.hide_select)
                object.hide_select = False
                object.select = False
            context.scene.objects.active = None

            # Make particle duplicates, parent and select them
            nodes_to_be_added = []
            if group[self.idx].use_include_particle_duplicates:
                for i, object in enumerate(group[self.idx].nodes):
                    if bpy.data.objects[object.name].type != "EMPTY":
                        context.scene.objects.active = bpy.data.objects[
                            object.name]
                        bpy.data.objects[object.name].select = True
                        bpy.ops.object.duplicates_make_real()
                        for object in context.selected_objects:
                            nodes_to_be_added.append(object)
                        bpy.ops.object.parent_set(type="OBJECT",
                                                  keep_transform=False)

                        for object in context.selected_objects:
                            object.select = False
                        bpy.data.objects[object.name].select = False
                        context.scene.objects.active = None
            for object in nodes_to_be_added:
                object.select = True

            # Select all other nodes from the group
            for i, object in enumerate(group[self.idx].nodes):
                if bpy.data.objects[object.name].type == "EMPTY":
                    self.convert_group_to_node(bpy.data.objects[object.name])
                else:
                    bpy.data.objects[object.name].select = True

            if group[self.idx].use_move_to_center:
                bpy.ops.object.location_clear()

            bpy.ops.object.transform_apply(location=group[self.idx].apply_loc,
                                           rotation=group[self.idx].apply_rot,
                                           scale=group[self.idx].apply_scale)
            bpy.ops.export_scene.dae(
                check_existing=True, filepath=path, filter_glob="*.dae",
                object_types=group[self.idx].object_types,
                use_export_selected=group[self.idx].use_export_selected,
                use_mesh_modifiers=group[self.idx].use_mesh_modifiers,
                use_tangent_arrays=group[self.idx].use_tangent_arrays,
                use_triangles=group[self.idx].use_triangles,
                use_copy_images=group[self.idx].use_copy_images,
                use_active_layers=group[self.idx].use_active_layers,
                use_anim=group[self.idx].use_anim,
                use_anim_action_all=group[self.idx].use_anim_action_all,
                use_anim_skip_noexp=group[self.idx].use_anim_skip_noexp,
                use_anim_optimize=group[self.idx].use_anim_optimize,
                anim_optimize_precision=group[
                    self.idx].anim_optimize_precision,
                use_metadata=group[self.idx].use_metadata)

            self.report({"INFO"},
                        "\"{}\" Group exported.".format(group[self.idx].name))
            msg = "Export Group {}".format(group[self.idx].name)

            bpy.ops.ed.undo_push(message="")
            bpy.ops.ed.undo()
            bpy.ops.ed.undo_push(message=msg)

        else:
            self.report({"INFO"}, "Define Export Name and Export Path.")

        return{"FINISHED"}


class add_export_group(bpy.types.Operator):
    bl_idname = "scene.godot_add_export_group"
    bl_label = "Adds a new export Group"
    bl_description = "Creates a new Export Group with the selected"\
                     " Objects assigned to it."

    def execute(self, context):
        scene = context.scene

        item = scene.godot_export_groups.add()
        item.name = "New Group"
        for object in context.selected_objects:
            node = item.nodes.add()
            node.name = object.name
        scene.godot_export_groups_index = len(scene.godot_export_groups)-1
        bpy.ops.ed.undo_push(message="Create New Export Group")

        return{"FINISHED"}


class del_export_group(bpy.types.Operator):
    bl_idname = "scene.godot_delete_export_group"
    bl_label = "Delets the selected export Group"
    bl_description = "Delets the active Export Group."

    def invoke(self, context, event):
        wm = context.window_manager

        return wm.invoke_confirm(self, event)

    def execute(self, context):
        scene = context.scene

        scene.godot_export_groups.remove(scene.godot_export_groups_index)
        if scene.godot_export_groups_index > 0:
            scene.godot_export_groups_index -= 1
        bpy.ops.ed.undo_push(message="Delete Export Group")

        return {"FINISHED"}


class godot_node_list(bpy.types.PropertyGroup):
    name = StringProperty()


class godot_export_groups(bpy.types.PropertyGroup):
    name = StringProperty(name="Group Name")
    export_name = StringProperty(name="scene_name")
    nodes = CollectionProperty(type=godot_node_list)
    export_path = StringProperty(subtype="DIR_PATH")
    active = BoolProperty(default=True, description="Export Group")

    object_types = EnumProperty(name="Object Types", options={"ENUM_FLAG"},
                                items=(("EMPTY", "Empty", ""),
                                       ("CAMERA", "Camera", ""),
                                       ("LAMP", "Lamp", ""),
                                       ("ARMATURE", "Armature", ""),
                                       ("MESH", "Mesh", ""),
                                       ("CURVE", "Curve", ""), ),
                                default={"EMPTY", "CAMERA", "LAMP",
                                         "ARMATURE", "MESH", "CURVE"})

    apply_scale = BoolProperty(name="Apply Scale",
                               description="Apply Scale before export.",
                               default=False)
    apply_rot = BoolProperty(name="Apply Rotation",
                             description="Apply Rotation before export.",
                             default=False)
    apply_loc = BoolProperty(name="Apply Location",
                             description="Apply Location before export.",
                             default=False)

    use_export_selected = BoolProperty(name="Selected Objects",
                                       description="Export only selected"
                                       "objects (and visible in active layers "
                                       "if that applies).", default=True)
    use_mesh_modifiers = BoolProperty(name="Apply Modifiers",
                                      description="Apply modifiers to mesh"
                                      " objects (on a copy!).", default=True)
    use_tangent_arrays = BoolProperty(name="Tangent Arrays",
                                      description="Export Tangent and Binormal"
                                      " arrays (for normalmapping).",
                                      default=False)
    use_triangles = BoolProperty(name="Triangulate",
                                 description="Export Triangles instead of"
                                 " Polygons.", default=False)

    use_copy_images = BoolProperty(name="Copy Images",
                                   description="Copy Images (create images/ "
                                   "subfolder)", default=False)
    use_active_layers = BoolProperty(name="Active Layers",
                                     description="Export only objects on the"
                                     " active layers.", default=True)
    use_anim = BoolProperty(name="Export Animation",
                            description="Export keyframe animation",
                            default=False)
    use_anim_action_all = BoolProperty(name="All Actions",
                                       description=("Export all actions for "
                                                    "the first armature found"
                                                    " in separate DAE files"),
                                       default=False)
    use_anim_skip_noexp = BoolProperty(name="Skip (-noexp) Actions",
                                       description="Skip exporting of"
                                       " actions whose name end in (-noexp)."
                                       " Useful to skip control animations.",
                                       default=True)
    use_anim_optimize = BoolProperty(name="Optimize Keyframes",
                                     description="Remove double keyframes",
                                     default=True)

    anim_optimize_precision = FloatProperty(
        name="Precision", description=("Tolerence for comparing double "
                                       "keyframes (higher for greater "
                                       "accuracy)"), min=1, max=16,
        soft_min=1, soft_max=16, default=6.0)

    use_metadata = BoolProperty(name="Use Metadata", default=True,
                                options={"HIDDEN"})
    use_include_particle_duplicates = BoolProperty(
        name="Include Particle Duplicates", default=True)

    use_move_to_center = BoolProperty(
        name="Move Objects to Center", default=False)


def register():
    bpy.utils.register_class(godot_export_manager)
    bpy.utils.register_class(godot_node_list)
    bpy.utils.register_class(godot_export_groups)
    bpy.utils.register_class(add_export_group)
    bpy.utils.register_class(del_export_group)
    bpy.utils.register_class(export_all_groups)
    bpy.utils.register_class(export_groups_autosave)
    bpy.utils.register_class(export_group)
    bpy.utils.register_class(add_objects_to_group)
    bpy.utils.register_class(del_objects_from_group)
    bpy.utils.register_class(select_group_objects)
    bpy.utils.register_class(UI_List_Godot)

    bpy.types.Scene.godot_export_groups = CollectionProperty(
        type=godot_export_groups)
    bpy.types.Scene.godot_export_groups_index = IntProperty(default=0, min=0)


def unregister():
    bpy.utils.unregister_class(godot_export_manager)
    bpy.utils.unregister_class(godot_node_list)
    bpy.utils.unregister_class(godot_export_groups)
    bpy.utils.unregister_class(export_groups_autosave)
    bpy.utils.unregister_class(add_export_group)
    bpy.utils.unregister_class(del_export_group)
    bpy.utils.unregister_class(export_all_groups)
    bpy.utils.unregister_class(export_group)
    bpy.utils.unregister_class(add_objects_to_group)
    bpy.utils.unregister_class(del_objects_from_group)
    bpy.utils.unregister_class(select_group_objects)
    bpy.utils.unregister_class(UI_List_Godot)


@persistent
def auto_export(dummy):
    bpy.ops.scene.godot_export_groups_autosave()

bpy.app.handlers.save_post.append(auto_export)

if __name__ == "__main__":
    register()
